package com.rules.admin.parser;

import com.alibaba.fastjson.JSON;
import com.gillion.grule.client.logger.LoggerAppenderHolder;
import com.gillion.grule.client.sync.DroolsRuleAssembly;
import com.gillion.grule.client.sync.DroolsRulePackage;
import com.gillion.grule.client.utils.BiFunction;
import com.gillion.grule.client.utils.Closure;
import com.gillion.grule.client.utils.EntityUtils;
import com.gillion.grule.client.utils.Streams;
import com.rules.admin.entity.*;
import com.rules.admin.expression.*;
import com.rules.admin.expression.operators.LetVariableExpression;
import com.rules.admin.expression.operators.Parenthesis;
import com.rules.admin.expression.operators.conditional.AndExpression;
import com.rules.admin.expression.operators.relational.Equals;
import com.rules.admin.expression.value.BooleanValue;
import com.rules.admin.expression.value.StringValue;
import com.rules.admin.service.*;
import com.rules.admin.service.decision.DecisionCellService;
import com.rules.admin.service.decision.DecisionColumnConfigService;
import com.rules.admin.service.decision.DecisionRowService;
import com.rules.admin.service.decision.DecisionTableService;
import com.rules.admin.vo.ViewAssemblyVO;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

@Service
public class DroolsRuleParser implements RuleParser<DroolsRulePackage> {
   @Autowired
   private ExpressionFactory expressionFactory;
   @Autowired
   private RulePackageService rulePackageService;
   @Autowired
   private RuleAssemblyService ruleAssemblyService;
   @Autowired
   private BusinessModelService businessModelService;
   @Autowired
   private RuleVariableService ruleVariableService;
   @Autowired
   private RuleService ruleService;
   @Autowired
   private DecisionTableService decisionTableService;
   @Autowired
   private DecisionRowService decisionRowService;
   @Autowired
   private DecisionColumnConfigService decisionColumnConfigService;
   @Autowired
   private DecisionCellService decisionCellService;
   @Autowired
   private TreeNodeService treeNodeService;
   @Autowired
   private TreeDedutionService treeDedutionService;
   private List<ExpressionVisitor> vistors = Lists.newArrayList();

   public DroolsRulePackage parser(final RulePackage rulePackage, final ParserContext parserContext) {
      final DroolsRulePackage droolsRulePackage = new DroolsRulePackage();
      droolsRulePackage.setArtifactId(rulePackage.getArtifactId());
      droolsRulePackage.setGroupId(rulePackage.getGroupId());
      droolsRulePackage.setVersion(rulePackage.getVersion());
      droolsRulePackage.setBusinessModelAttributes(this.businessModelService.getModelAttributes(rulePackage.getBusinessModelId()));
      parserContext.setArtifactId(rulePackage.getArtifactId());
      parserContext.setGroupId(rulePackage.getGroupId());
      LoggerAppenderHolder.log("开始构造规则根文件内容...", new Object[0]);
      final RulePackageExpression rulePackageExpression = this.expressionFactory.createDefauleRulePackage(rulePackage.getGroupId(), this.vistors);
      RuleExpression ruleExpression = this.buildRootRuleExpression(rulePackage);
      final Deduction deduction = new Deduction();
      List<ViewAssemblyVO> viewAssemblyVOs = this.rulePackageService.getViewAssemblyVOs(rulePackage.getId());
      Streams.of(viewAssemblyVOs).forEach(new Closure<ViewAssemblyVO>() {
         public void execute(ViewAssemblyVO viewAssemblyVO) {
            boolean flag;
            if (viewAssemblyVO.getType() == 1) {
               LoggerAppenderHolder.log("开始构造决策表内容:{}", new Object[]{viewAssemblyVO.getName()});
               flag = DroolsRuleParser.this.buildDecisionTable(viewAssemblyVO, rulePackage, droolsRulePackage);
            } else if (viewAssemblyVO.getType() == 2) {
               LoggerAppenderHolder.log("开始构造决策树内容:{}", new Object[]{viewAssemblyVO.getName()});
               flag = DroolsRuleParser.this.buildDecisionTree(viewAssemblyVO, rulePackage, droolsRulePackage);
            } else {
               LoggerAppenderHolder.log("开始构造规则集内容:{}", new Object[]{viewAssemblyVO.getName()});
               flag = DroolsRuleParser.this.buildSingleAssembly(viewAssemblyVO, rulePackage, droolsRulePackage, parserContext);
            }

            if (flag) {
               Expression executeExpression = new ExecuteExpression("DroolsRuleUtils", "execute", new Expression[]{new Property("$droolsRulePackage"), new Property("$context"), new Property("$model"), new StringValue(viewAssemblyVO.getName())});
               if (StringUtils.isEmpty(viewAssemblyVO.getConditions())) {
                  deduction.addDeduction(executeExpression);
               } else {
                  IfExpression ifExpression = new IfExpression();
                  Map<String, Object> condition = (Map)JSON.parseObject(viewAssemblyVO.getConditions(), Map.class);
                  if (condition.isEmpty()) {
                     ifExpression.setConditionExpression(new Equals(new ExecuteExpression("$context", "isBreakPackage"), new BooleanValue("false")));
                  } else {
                     AndExpression andExpression = new AndExpression();
                     andExpression.setLeftExpression(new Equals(new ExecuteExpression("$context", "isBreakPackage"), new BooleanValue("false")));
                     andExpression.setRightExpression(ParserUtils.parseCondition(condition));
                     ifExpression.setConditionExpression(andExpression);
                  }

                  ifExpression.addExecuteExpression(executeExpression);
                  deduction.addDeduction(ifExpression);
               }
            }

         }
      });
      ruleExpression.setDeduction(deduction);
      rulePackageExpression.addRule(ruleExpression);
      Streams.of(this.vistors).forEach(new Closure<ExpressionVisitor>() {
         public void execute(ExpressionVisitor visitor) {
            rulePackageExpression.accept(visitor);
         }
      });
      droolsRulePackage.setContent(rulePackageExpression.toString());
      LoggerAppenderHolder.log("最终生成的入口规则为：\n{}", new Object[]{droolsRulePackage.getContent()});
      return droolsRulePackage;
   }

   private boolean buildDecisionTable(final ViewAssemblyVO table, RulePackage rulePackage, DroolsRulePackage droolsRulePackage) {
      DroolsRuleAssembly droolsRuleAssembly = new DroolsRuleAssembly();
      List<DecisionRow> rows = this.decisionRowService.getEnableRows(table.getId());
      final RulePackageExpression assemblyRulePackageExpression = this.expressionFactory.createDefauleRulePackage(rulePackage.getGroupId(), this.vistors);
      if (CollectionUtils.isEmpty(rows)) {
         return false;
      } else {
         final AtomicInteger counter = new AtomicInteger(0);
         Streams.of(rows).forEach(new Closure<DecisionRow>() {
            public void execute(DecisionRow row) {
               if (StringUtils.isNotBlank(row.getDedution())) {
                  assemblyRulePackageExpression.addRule(DroolsRuleParser.this.buildDecisionRow(table, row, counter));
               }

            }
         });
         Streams.of(this.vistors).forEach(new Closure<ExpressionVisitor>() {
            public void execute(ExpressionVisitor visitor) {
               assemblyRulePackageExpression.accept(visitor);
            }
         });
         LoggerAppenderHolder.log("最终生成的规则表：{}，内容为：\n\n{}", new Object[]{table.getName(), assemblyRulePackageExpression.toString()});
         if (CollectionUtils.isEmpty(assemblyRulePackageExpression.getRuleExpressions())) {
            return false;
         } else {
            droolsRuleAssembly.setContent(assemblyRulePackageExpression.toString());
            droolsRulePackage.addAssembly(table.getName(), droolsRuleAssembly);
            return true;
         }
      }
   }

   private RuleExpression buildDecisionRow(ViewAssemblyVO table, DecisionRow row, AtomicInteger counter) {
      final RuleExpression ruleExpression = this.expressionFactory.createAssemblyRule();
      ruleExpression.setRuleName(table.getName() + "_" + counter.getAndAdd(1));
      Byte priority = row.getPriority();
      if (priority == null) {
         priority = 1;
      }

      ruleExpression.getAttributes().add(new Attribute("salience " + priority));
      Fact contextFact = (Fact)ruleExpression.getFacts().get(0);
      LetVariableExpression letVariableExpression = new LetVariableExpression();
      letVariableExpression.setLeftExpression(new VariableExpression("$variable"));
      ExecuteExpression executeExpression = new ExecuteExpression("getDecisionVariable", new Expression[]{new StringValue(table.getName())});
      letVariableExpression.setRightExpression(executeExpression);
      contextFact.addCondition(letVariableExpression);
      contextFact.addCondition(new Equals(new ExecuteExpression("isBreakPackage", new Expression[0]), new BooleanValue("false")));
      contextFact.addCondition(new Equals(new ExecuteExpression("isBreakAssembly", new Expression[]{new StringValue(table.getName())}), new BooleanValue("false")));
      Fact fact = this.expressionFactory.createDefaultFact();
      List<DecisionCell> cells = this.decisionCellService.getCellsByRowId(row.getId());
      AndExpression condition = (AndExpression)Streams.of(cells).reduce(new BiFunction<AndExpression, DecisionCell>() {
         public AndExpression apply(AndExpression andExpression, DecisionCell cell) {
            String stConditions = cell.getConditions();
            if (StringUtils.isNotBlank(stConditions) && !"{}".equals(stConditions)) {
               Map<String, Object> conditions = (Map) EntityUtils.readObjectFromJSONString(stConditions, Map.class);
               Parenthesis parenthesis = new Parenthesis(ParserUtils.parseCondition(conditions));
               if (andExpression == null) {
                  andExpression = new AndExpression();
                  andExpression.setLeftExpression(new BooleanValue(Boolean.TRUE.toString()));
                  andExpression.setRightExpression(parenthesis);
                  return andExpression;
               } else {
                  AndExpression result = new AndExpression();
                  result.setLeftExpression(andExpression);
                  result.setRightExpression(parenthesis);
                  return result;
               }
            } else {
               return andExpression;
            }
         }
      });
      if (condition != null) {
         fact.addCondition(condition);
      }

      ruleExpression.addFact(fact);
      List<Map<String, Object>> executes = (List)EntityUtils.readObjectFromJSONString(row.getDedution(), List.class);
      List<Expression> deductionExpressions = Streams.of(executes).map(new Function<Map<String, Object>, Expression>() {
         public Expression apply(Map<String, Object> input) {
            return ParserUtils.parseCondition(input);
         }
      }).collectToList();
      Deduction deduction = new Deduction();
      deduction.setExpressions(deductionExpressions);
      ruleExpression.setDeduction(deduction);
      Streams.of(this.vistors).forEach(new Closure<ExpressionVisitor>() {
         public void execute(ExpressionVisitor visitor) {
            ruleExpression.accept(visitor);
         }
      });
      return ruleExpression;
   }

   private RuleExpression buildRootRuleExpression(RulePackage rulePackage) {
      RuleExpression ruleExpression = this.expressionFactory.createPackageRule();
      ruleExpression.setRuleName("ROOT");
      Fact contextFact = (Fact)ruleExpression.getFacts().get(0);
      LetVariableExpression letVariableExpression = new LetVariableExpression();
      letVariableExpression.setLeftExpression(new VariableExpression("$variable"));
      ExecuteExpression executeExpression = new ExecuteExpression("getAssemblyVariable", new Expression[0]);
      letVariableExpression.setRightExpression(executeExpression);
      contextFact.addCondition(letVariableExpression);
      contextFact.addCondition(new Equals(new ExecuteExpression("isBreakPackage", new Expression[0]), new BooleanValue("false")));
      String conditionStr = rulePackage.getConditions();
      if (!StringUtils.isEmpty(conditionStr)) {
         Fact fact = this.expressionFactory.createDefaultFact();
         if (!"\"{}\"".equals(conditionStr)) {
            Map<String, Object> condition = (Map)JSON.parseObject(conditionStr, Map.class);
            if (!condition.isEmpty()) {
               fact.addCondition((BinaryExpression)ParserUtils.parseCondition(condition));
            }
         }

         ruleExpression.addFact(fact);
      }

      return ruleExpression;
   }

   private boolean buildSingleAssembly(final ViewAssemblyVO ruleAssembly, RulePackage rulePackage, DroolsRulePackage droolsRulePackage, final ParserContext parserContext) {
      DroolsRuleAssembly droolsRuleAssembly = new DroolsRuleAssembly();
      List<Rule> rules = this.ruleService.getRuleListByAssemblyIdAndStatus(ruleAssembly.getId());
      final RulePackageExpression assemblyRulePackageExpression = this.expressionFactory.createDefauleRulePackage(rulePackage.getGroupId(), this.vistors);
      if (CollectionUtils.isEmpty(rules)) {
         return false;
      } else {
         List<RuleExpression> ruleExpressions = (List)Streams.of(rules).reduce(new BiFunction<List<RuleExpression>, Rule>() {
            public List<RuleExpression> apply(List<RuleExpression> ruleExpressions, Rule rule) {
               LoggerAppenderHolder.log("开始构造规则：{}内容", new Object[]{rule.getName()});
               if (ruleExpressions == null) {
                  ruleExpressions = Lists.newArrayList();
               }

               List<RuleExpression> buildRuleExpressions = DroolsRuleParser.this.buildMutiRuleExpression(rule, ruleAssembly.getName(), parserContext);
               ((List)ruleExpressions).addAll(buildRuleExpressions);
               return (List)ruleExpressions;
            }
         });
         assemblyRulePackageExpression.setRuleExpressions(ruleExpressions);
         Streams.of(this.vistors).forEach(new Closure<ExpressionVisitor>() {
            public void execute(ExpressionVisitor visitor) {
               assemblyRulePackageExpression.accept(visitor);
            }
         });
         LoggerAppenderHolder.log("最终生成的规则集：{}，内容为：\n\n{}", new Object[]{ruleAssembly.getName(), assemblyRulePackageExpression.toString()});
         droolsRuleAssembly.setContent(assemblyRulePackageExpression.toString());
         droolsRulePackage.addAssembly(ruleAssembly.getName(), droolsRuleAssembly);
         return true;
      }
   }

   private List<RuleExpression> buildMutiRuleExpression(final Rule input, final String assemblyName, final ParserContext parserContext) {
      List<RuleExpression> ruleExpressions = Lists.newArrayList();
      String expressionStr = input.getExpression();
      Map<String, Object> expression = (Map)JSON.parseObject(expressionStr, Map.class);
      Map<String, Object> ifConfig = MapUtils.getMap(expression, "if");
      final AtomicInteger counter = new AtomicInteger(0);
      parserContext.setRuleId(input.getId());
      RuleExpression ruleExpression = this.buildSingleRuleExpression(ifConfig, input, counter, assemblyName, parserContext);
      ruleExpressions.add(ruleExpression);
      List<Map<String, Object>> elseIfsConfig = (List)ifConfig.get("ifElses");
      if (elseIfsConfig != null) {
         List<RuleExpression> elseIfExpressions = (List)Streams.of(elseIfsConfig).reduce(new BiFunction<List<RuleExpression>, Map<String, Object>>() {
            public List<RuleExpression> apply(List<RuleExpression> ruleExpressions, Map<String, Object> map) {
               if (ruleExpressions == null) {
                  ruleExpressions = Lists.newArrayList();
               }

               ((List)ruleExpressions).add(DroolsRuleParser.this.buildSingleRuleExpression(map, input, counter, assemblyName, parserContext));
               return (List)ruleExpressions;
            }
         });
         ruleExpressions.addAll(elseIfExpressions);
      }

      Map<String, Object> elseConfig = MapUtils.getMap(ifConfig, "else");
      if (elseConfig != null) {
         RuleExpression elseExpression = this.buildSingleRuleExpression(elseConfig, input, counter, assemblyName, parserContext);
         ruleExpressions.add(elseExpression);
      }

      return ruleExpressions;
   }

   private RuleExpression buildSingleRuleExpression(Map<String, Object> map, Rule rule, AtomicInteger counter, String assemblyName, ParserContext parserContext) {
      final RuleExpression ruleExpression = this.expressionFactory.createAssemblyRule();
      ruleExpression.setRuleName(rule.getName() + "_" + counter.getAndAdd(1));
      ruleExpression.getAttributes().add(new Attribute("salience " + rule.getPriority()));
      Fact contextFact = (Fact)ruleExpression.getFacts().get(0);
      LetVariableExpression letVariableExpression = new LetVariableExpression();
      letVariableExpression.setLeftExpression(new VariableExpression("$variable"));
      ExecuteExpression executeExpression = new ExecuteExpression("getRuleVariable", new Expression[]{new StringValue(assemblyName), new StringValue(rule.getName())});
      letVariableExpression.setRightExpression(executeExpression);
      contextFact.addCondition(letVariableExpression);
      contextFact.addCondition(new Equals(new ExecuteExpression("isBreakPackage", new Expression[0]), new BooleanValue("false")));
      contextFact.addCondition(new Equals(new ExecuteExpression("isBreakAssembly", new Expression[]{new StringValue(assemblyName)}), new BooleanValue("false")));
      Map<String, Object> condition = MapUtils.getMap(map, "condition");
      Fact fact = this.expressionFactory.createDefaultFact();
      fact.addCondition((BinaryExpression)ParserUtils.parseCondition(condition));
      ruleExpression.addFact(fact);
      List<Map<String, Object>> executes = (List)MapUtils.getObject(map, "then");
      List<Expression> deductionExpressions = new ArrayList();
      int i = 0;
      Iterator var15 = executes.iterator();

      while(var15.hasNext()) {
         Map<String, Object> execute = (Map)var15.next();
         ++i;
         if (this.vistors.size() > 0 && parserContext.isSave()) {
            deductionExpressions.add(new ParserExpression(parserContext));
         }

         deductionExpressions.add(ParserUtils.parseCondition(execute));
         parserContext.setDeductionNo(parserContext.getDeductionNo() + 1);
         if (this.vistors.size() > 0 && parserContext.isSave() && i == executes.size()) {
            deductionExpressions.add(new ParserExpression(parserContext));
         }
      }

      parserContext.setDeductionNo(0);
      Deduction deduction = new Deduction();
      deduction.setExpressions(deductionExpressions);
      ruleExpression.setDeduction(deduction);
      Streams.of(this.vistors).forEach(new Closure<ExpressionVisitor>() {
         public void execute(ExpressionVisitor visitor) {
            ruleExpression.accept(visitor);
         }
      });
      return ruleExpression;
   }

   private boolean buildDecisionTree(final ViewAssemblyVO tree, RulePackage rulePackage, DroolsRulePackage droolsRulePackage) {
      DroolsRuleAssembly droolsRuleAssembly = new DroolsRuleAssembly();
      List<TreeNode> treeNodes = this.treeNodeService.getTreeNodesByTreeId(tree.getId());
      List<TreeDedution> treeDedutions = this.treeDedutionService.getTreeNodesByTreeId(tree.getId());
      final RulePackageExpression assemblyRulePackageExpression = this.expressionFactory.createDefauleRulePackage(rulePackage.getGroupId(), this.vistors);
      if (!CollectionUtils.isEmpty(treeNodes) && !CollectionUtils.isEmpty(treeDedutions)) {
         final Map<Long, TreeNode> treeNodeMap = Maps.newHashMap();
         Iterator var9 = treeNodes.iterator();

         while(var9.hasNext()) {
            TreeNode treeNode = (TreeNode)var9.next();
            treeNodeMap.put(treeNode.getId(), treeNode);
         }

         final AtomicInteger counter = new AtomicInteger(0);
         Streams.of(treeDedutions).forEach(new Closure<TreeDedution>() {
            public void execute(TreeDedution treeDedution) {
               if (StringUtils.isNotBlank(treeDedution.getDedution())) {
                  List<TreeNode> treeNodes = DroolsRuleParser.this.extractTreeNodes(treeDedution, treeNodeMap);
                  assemblyRulePackageExpression.addRule(DroolsRuleParser.this.buildTreeDedution(tree, treeDedution, treeNodes, counter));
               }

            }
         });
         Streams.of(this.vistors).forEach(new Closure<ExpressionVisitor>() {
            public void execute(ExpressionVisitor visitor) {
               assemblyRulePackageExpression.accept(visitor);
            }
         });
         LoggerAppenderHolder.log("最终生成的决策树：{}，内容为：\n\n{}", new Object[]{tree.getName(), assemblyRulePackageExpression.toString()});
         if (CollectionUtils.isEmpty(assemblyRulePackageExpression.getRuleExpressions())) {
            return false;
         } else {
            droolsRuleAssembly.setContent(assemblyRulePackageExpression.toString());
            droolsRulePackage.addAssembly(tree.getName(), droolsRuleAssembly);
            return true;
         }
      } else {
         return false;
      }
   }

   private List<TreeNode> extractTreeNodes(TreeDedution dedution, Map<Long, TreeNode> treeNodeMap) {
      List<TreeNode> treeNodes = Lists.newArrayList();
      if (dedution.getNodeId() != null) {
         TreeNode node = (TreeNode)treeNodeMap.get(dedution.getNodeId());
         treeNodes.add(node);

         while(node.getParentId() != null) {
            node = (TreeNode)treeNodeMap.get(node.getParentId());
            treeNodes.add(node);
         }
      }

      Collections.reverse(treeNodes);
      return treeNodes;
   }

   private RuleExpression buildTreeDedution(ViewAssemblyVO tree, TreeDedution treeDedution, List<TreeNode> treeNodes, AtomicInteger counter) {
      final RuleExpression ruleExpression = this.expressionFactory.createAssemblyRule();
      ruleExpression.setRuleName(tree.getName() + "_" + counter.getAndAdd(1));
      int priority = counter.intValue();
      ruleExpression.getAttributes().add(new Attribute("salience " + priority));
      Fact contextFact = (Fact)ruleExpression.getFacts().get(0);
      LetVariableExpression letVariableExpression = new LetVariableExpression();
      letVariableExpression.setLeftExpression(new VariableExpression("$variable"));
      ExecuteExpression executeExpression = new ExecuteExpression("getDecisionTreeVariable", new Expression[]{new StringValue(tree.getName())});
      letVariableExpression.setRightExpression(executeExpression);
      contextFact.addCondition(letVariableExpression);
      contextFact.addCondition(new Equals(new ExecuteExpression("isBreakPackage", new Expression[0]), new BooleanValue("false")));
      contextFact.addCondition(new Equals(new ExecuteExpression("isBreakAssembly", new Expression[]{new StringValue(tree.getName())}), new BooleanValue("false")));
      Fact fact = this.expressionFactory.createDefaultFact();
      AndExpression condition = (AndExpression)Streams.of(treeNodes).reduce(new BiFunction<AndExpression, TreeNode>() {
         public AndExpression apply(AndExpression andExpression, TreeNode treeNode) {
            String stConditions = treeNode.getConditions();
            if (StringUtils.isNotBlank(stConditions) && !"{}".equals(stConditions)) {
               Map<String, Object> conditions = (Map)EntityUtils.readObjectFromJSONString(stConditions, Map.class);
               Parenthesis parenthesis = new Parenthesis(ParserUtils.parseCondition(conditions));
               if (andExpression == null) {
                  andExpression = new AndExpression();
                  andExpression.setLeftExpression(new BooleanValue(Boolean.TRUE.toString()));
                  andExpression.setRightExpression(parenthesis);
                  return andExpression;
               } else {
                  AndExpression result = new AndExpression();
                  result.setLeftExpression(andExpression);
                  result.setRightExpression(parenthesis);
                  return result;
               }
            } else {
               return andExpression;
            }
         }
      });
      if (condition != null) {
         fact.addCondition(condition);
      }

      ruleExpression.addFact(fact);
      List<Map<String, Object>> executes = (List)EntityUtils.readObjectFromJSONString(treeDedution.getDedution(), List.class);
      List<Expression> deductionExpressions = Streams.of(executes).map(new Function<Map<String, Object>, Expression>() {
         public Expression apply(Map<String, Object> input) {
            return ParserUtils.parseCondition(input);
         }
      }).collectToList();
      Deduction deduction = new Deduction();
      deduction.setExpressions(deductionExpressions);
      ruleExpression.setDeduction(deduction);
      Streams.of(this.vistors).forEach(new Closure<ExpressionVisitor>() {
         public void execute(ExpressionVisitor visitor) {
            ruleExpression.accept(visitor);
         }
      });
      return ruleExpression;
   }

   public void setVistors(List<ExpressionVisitor> vistors) {
      this.vistors = vistors;
   }
}
